package channel

import (
	"fmt"
	"time"
)

/** channel 信道

什么是信道？
信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端，通过使用信道，数据也可以从一端发送，在另一端接收。
信道的声明
所有信道都关联了一个类型。信道只能运输这种类型的数据，而运输其他类型的数据都是非法的。
chan T表示 T类型的信道。
信道的零值为nil。信道的零值没有什么用，应该像对 map 和切片所做的那样，用make来定义信道。

简短声明通常也是一种定义信道的简洁有效的方法：
	a := make(chan int)

通过信道发送和接收数据语法：
	data := <- a // 读取信道
	aa <- data // 写入信道 a

关键点：
	<-chan T 是只读信道（只能接收）
	chan<- T 是只写信道（只能发送）
	chan T 是双向信道（可读可写

*/

func Test() {

	// 示例1 创建无缓冲信道
	//case1()

	// 示例2 go协程 写入信道消息，主程序读取信道内容
	//case2()

	// 示例3 死锁
	//case3()

	// 示例4 创建有缓冲信道
	//case4()

}

// 示例1 创建无缓冲信道
func case1() {

	// 定义无缓冲信道
	done := make(chan bool)

	// go 使用协程
	go hello(done)

	// 读取信道 这行代码发生了阻塞
	<-done
	fmt.Println("case1() end...")

}

func hello(done chan bool) {
	fmt.Println("这是一个 go协程", done)
	done <- true //写入信道 done
}

// 示例2 go协程 写入信道消息，主程序读取信道内容
func case2() {

	// 创建无缓冲信道
	done := make(chan string)

	// 启动go 协程
	go func() {
		time.Sleep(2 * time.Second)
		done <- "hello from goroutine" //写入信道
	}()

	msg := <-done //读取信道内容，会阻塞程序
	fmt.Println("执行完毕，接收：", msg)
}

// 示例3 死锁
func case3() {
	ch := make(chan string)
	ch <- "hello"
	// 这个示例只有写入信道，没有读取信道，就会报错：fatal error: all goroutines are asleep - deadlock!
}

// 示例4 创建有缓冲信道
func case4() {
	done := make(chan int, 2)
	done <- 2
	done <- 4
	// done <- 5 定义了信道容量2，只能写入2个数字，超出容量写入会出现死锁 ：fatal error: all goroutines are asleep - deadlock!
	fmt.Println(<-done)
	fmt.Println(<-done)

	// 想要这次的写入能够进行下去，必须有其它协程来读取这个信道的数据，才能继续写入！！
	done <- 5
	fmt.Println(<-done)

}
